﻿using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using MoreEvent.Common.Players;
using System;
using System.Collections.Generic;
using System.IO;
using Terraria;
using Terraria.GameContent;
using Terraria.ID;
using Terraria.Localization;
using Terraria.ModLoader;
using Terraria.ModLoader.IO;

namespace MoreEvent.Common.Systems
{
    public class EventSystem : ModSystem
    {
        private bool _wasDayTime;
        public delegate void TimeDelegate(bool day);
        public static event TimeDelegate TimeChanged;
        public static bool AuroraActive => AuroraType != Default && ActiveEvents.Contains(7);
        public static byte AuroraType { get; private set; } = Default;
        public const byte Default = byte.MaxValue;
        public static bool DayTimeLast;
        public static bool DayTimeSwitched;
        public static List<int> ActiveEvents = [];
        private static readonly Dictionary<int, (string chinese, string english, Color color)> EventData = new()
        {
            { 1, ("流星雨", "Meteor Shower", Color.White) },
            { 2, ("疯狂星期四", "Crazy Thursday", Color.White) },
            { 3, ("薯条雨", "French Fry Rain", Color.White) },
            { 4, ("刀雨", "Knife Rain", Color.White) },
            { 5, ("热浪", "Heat Wave", Color.White) },
            { 6, ("寒潮", "Cold Front", Color.White) },
            { 7, ("极光", "Aurora", Color.White) },
        };

        private static readonly Dictionary<int, string> EventIcons = new()
        {
            { 1, "MoreEvent/Assets/Images/Misc/Icons/MeteorShower_Icon" },
            { 2, "MoreEvent/Assets/Images/Misc/Icons/CrazyThursday_Icon" },
            { 3, "MoreEvent/Assets/Images/Misc/Icons/FriesRain_Icon" },
            { 4, "MoreEvent/Assets/Images/Misc/Icons/KnifeRain_Icon" },
            { 5, "MoreEvent/Assets/Images/Misc/Icons/HeatWave_Icon" },
            { 6, "MoreEvent/Assets/Images/Misc/Icons/ColdFront_Icon" },
            { 7, "MoreEvent/Assets/Images/Misc/Icons/Aurora_Icon" },
        };

        private static readonly Dictionary<int, List<int>> SubEvents = new()
        {
            { 2, new List<int> { 3 } }
        };
        private static string GetEventName(int eventId)
        {
            if (EventData.TryGetValue(eventId, out var eventInfo))
            {
                return GameCulture.FromCultureName(GameCulture.CultureName.Chinese).IsActive ?
                    eventInfo.chinese : eventInfo.english;
            }
            return $"Event {eventId}";
        }
        private static string GetEventStartMessage(int eventId)
        {
            return eventId switch
            {
                2 => GameCulture.FromCultureName(GameCulture.CultureName.Chinese).IsActive ?
                    "疯狂星期四到了！V我50！" : "Crazy Thursday! V me 50!",
                _ => GetEventName(eventId)
            };
        }
        private static string GetEventEndMessage(int eventId)
        {
            return eventId switch
            {
                2 => GameCulture.FromCultureName(GameCulture.CultureName.Chinese).IsActive ?
                    "疯狂星期四结束了，下周再见！" : "Crazy Thursday ended, see you next week!",
                _ => $"{GetEventName(eventId)} ended"
            };
        }

        public override void Load()
        {
            TimeChanged += UpdateAurora;
        }
        private static void UpdateAurora(bool day)
        {
            if (Main.netMode != NetmodeID.MultiplayerClient)
            {
                AuroraType = Default;
                if (!day && Main.rand.NextBool(3))
                {
                    AuroraType = (byte)Main.rand.Next([1, 2, 3, 5]);
                }
            }
        }

        public override void ResetNearbyTileEffects()
        {
            Main.LocalPlayer.GetModPlayer<EventPlayer>().Reset();
        }

        public override void ClearWorld() => AuroraType = Default;

        public override void OnWorldLoad()
        {
            _wasDayTime = Main.dayTime;
            ActiveEvents = [];
        }

        public override void PostUpdateEverything()
        {
            if (Main.dayTime != _wasDayTime)
            {
                TimeChanged?.Invoke(Main.dayTime);
            }
            _wasDayTime = Main.dayTime;
        }

        public override void Unload()
        {
            TimeChanged = null;
        }

        public override void PostUpdateWorld()
        {
            if (Main.dayTime != DayTimeLast)
            {
                DayTimeSwitched = true;
            }
            else
            {
                DayTimeSwitched = false;
            }
            DayTimeLast = Main.dayTime;

            if (DayTimeSwitched)
            {
                ActiveEvents = [];
            }
            if (DayTimeSwitched && ActiveEvents.Count == 0)
            {
                // 流星雨
                if (!Main.dayTime && Main.rand.NextBool(12))
                {
                    StartEvent(1);
                }
                else
                {
                    EndEvent(1);
                }
                if (ActiveEvents.Contains(1))
                {
                    EndEvent(7);
                }

                // 疯狂星期四
                if (DateTime.Now.DayOfWeek == DayOfWeek.Thursday)
                {
                    StartEvent(2);
                    Main.NewText(GetEventStartMessage(2), Color.White);
                }
                else if (ActiveEvents.Contains(2) && DateTime.Now.DayOfWeek != DayOfWeek.Thursday)
                {
                    EndEvent(2);
                    Main.NewText(GetEventEndMessage(2), Color.White);
                }

                // 薯条雨
                if (ActiveEvents.Contains(2))
                {
                    if (Main.raining && Main.rand.NextBool(8))
                    {
                        StartEvent(3);
                    }
                    if (!Main.raining)
                    {
                        EndEvent(3);
                    }
                }
                if (!ActiveEvents.Contains(2) || ActiveEvents.Contains(4))
                {
                    ActiveEvents.Remove(3);
                }

                // 刀雨
                if (Main.raining && Main.rand.NextBool(25))
                {
                    StartEvent(4);
                }
                else
                {
                    EndEvent(4);
                }

                // 薯条雨与刀雨互斥
                if (ActiveEvents.Contains(3))
                {
                    ActiveEvents.Remove(4);
                }

                // 热浪
                if (!Main.raining && Main.dayTime && Main.rand.NextBool(25) && !Main.LocalPlayer.ZoneSnow)
                {
                    StartEvent(5);
                }
                else
                {
                    EndEvent(5);
                }

                // 寒潮
                if (!Main.raining && Main.rand.NextBool(13) && Main.dayTime && !Main.LocalPlayer.ZoneDesert)
                {
                    StartEvent(6);
                }
                else
                {
                    EndEvent(5);
                    EndEvent(6);
                }

                // 极光
                if (!Main.dayTime && (Main.LocalPlayer.ZoneSnow || Main.LocalPlayer.ZoneSkyHeight) && !ActiveEvents.Contains(1))
                {
                    StartEvent(7);
                }
                else if (Main.dayTime || ActiveEvents.Contains(1))
                {
                    EndEvent(7);
                }
                if (ActiveEvents.Contains(7))
                {
                    EndEvent(1);
                }
            }
        }

        public void StartEvent(int id)
        {
            if (Main.netMode != NetmodeID.SinglePlayer)
            {
                ModPacket packet = Mod.GetPacket(256);
                packet.Write(0);
                packet.Write(id);
                packet.Send(-1, -1);
            }
            if (!ActiveEvents.Contains(id))
            {
                ActiveEvents.Add(id);
                string eventName = GetEventName(id);
                if (EventData.TryGetValue(id, out var eventInfo))
                {
                    EventTextSystem.ShowEventText($"{eventName} ", eventInfo.color, 180);
                }
            }
        }

        public void EndEvent(int id)
        {
            if (Main.netMode != NetmodeID.SinglePlayer)
            {
                ModPacket packet = Mod.GetPacket(256);
                packet.Write(1);
                packet.Write(id);
                packet.Send(-1, -1);
            }
            ActiveEvents.Remove(id);
        }

        public override void SaveWorldData(TagCompound tag)
        {
            tag[nameof(AuroraType)] = AuroraType;
            tag["ActiveEvents"] = ActiveEvents;
        }

        public override void LoadWorldData(TagCompound tag)
        {
            AuroraType = tag.GetByte(nameof(AuroraType));
            ActiveEvents = tag.Get<List<int>>("ActiveEvents") ?? [];
        }

        public override void PostDrawInterface(SpriteBatch spriteBatch)
        {
            DrawActiveEvents(spriteBatch);
        }

        private static void DrawActiveEvents(SpriteBatch spriteBatch)
        {
            if (ActiveEvents.Count != 0)
            {
                var mainEvents = GetMainEvents();
                if (mainEvents.Count != 0)
                {
                    Vector2 startPos = new(Main.screenWidth / 2f, 20f);
                    float totalWidth = CalculateTotalWidth(mainEvents);
                    Vector2 currentPos = startPos - new Vector2(totalWidth / 2f, 0f);
                    foreach (var eventId in mainEvents)
                    {
                        DrawEventWithSubEvents(spriteBatch, eventId, ref currentPos);
                    }
                }
            }
        }

        private static List<int> GetMainEvents()
        {
            var mainEvents = new List<int>();
            foreach (int eventId in ActiveEvents)
            {
                bool isSubEvent = false;
                foreach (var subEventList in SubEvents.Values)
                {
                    if (subEventList.Contains(eventId))
                    {
                        isSubEvent = true;
                        break;
                    }
                }

                if (!isSubEvent)
                {
                    mainEvents.Add(eventId);
                }
            }
            return mainEvents;
        }

        private static float CalculateTotalWidth(List<int> mainEvents)
        {
            float totalWidth = 0f;
            foreach (var eventId in mainEvents)
            {
                totalWidth += GetEventWidth(eventId);
                if (SubEvents.TryGetValue(eventId, out var subEvents))
                {
                    foreach (var subEventId in subEvents)
                    {
                        if (ActiveEvents.Contains(subEventId))
                        {
                            totalWidth += GetEventWidth(subEventId, true);
                        }
                    }
                }
                totalWidth += 20f;
            }
            return totalWidth - 20f;
        }

        private static void DrawEventWithSubEvents(SpriteBatch spriteBatch, int mainEventId, ref Vector2 position)
        {
            DrawSingleEvent(spriteBatch, mainEventId, position, false);
            position.X += GetEventWidth(mainEventId) + 5f;
            if (SubEvents.TryGetValue(mainEventId, out var subEvents))
            {
                DrawSeparator(spriteBatch, position);
                position.X += 15f;

                foreach (var subEventId in subEvents)
                {
                    if (ActiveEvents.Contains(subEventId))
                    {
                        DrawSingleEvent(spriteBatch, subEventId, position, true);
                        position.X += GetEventWidth(subEventId, true) + 5f;
                    }
                }
            }
            position.X += 15f;
        }

        private static void DrawSingleEvent(SpriteBatch spriteBatch, int eventId, Vector2 position, bool isSubEvent)
        {
            float scale = isSubEvent ? 1.0f : 1.2f;
            Color color = isSubEvent ? Color.LightGray : Color.White;
            float totalHeight = 0f;
            float iconWidth = 0f;

            if (EventIcons.TryGetValue(eventId, out string iconPath))
            {
                Texture2D icon = ModContent.Request<Texture2D>(iconPath).Value;
                totalHeight = Math.Max(totalHeight, icon.Height * scale);
                iconWidth = icon.Width * scale;
            }

            string eventName = GetEventName(eventId);
            Vector2 textSize = FontAssets.MouseText.Value.MeasureString(eventName) * scale * 0.8f;
            totalHeight = Math.Max(totalHeight, textSize.Y);

            if (EventIcons.TryGetValue(eventId, out string iconPath2))
            {
                Texture2D icon = ModContent.Request<Texture2D>(iconPath2).Value;
                Vector2 iconPos = position + new Vector2(-5f, totalHeight / 2f);
                spriteBatch.Draw(icon, iconPos, null, color, 0f, new Vector2(0f, icon.Height / 2f), scale, SpriteEffects.None, 0f);
            }

            Vector2 textPos = position + new Vector2(iconWidth + 10f * scale, totalHeight / 2f);
            Utils.DrawBorderString(spriteBatch, eventName, textPos + new Vector2(1f, 1f), Color.Black * 0.6f, scale * 0.8f, 0.5f, 0.5f);
            Utils.DrawBorderString(spriteBatch, eventName, textPos, color, scale * 0.8f, 0.5f, 0.5f);
        }

        private static float GetEventWidth(int eventId, bool isSubEvent = false)
        {
            float scale = isSubEvent ? 1.0f : 1.2f;
            float width = 0f;

            if (EventIcons.TryGetValue(eventId, out string iconPath))
            {
                Texture2D icon = ModContent.Request<Texture2D>(iconPath).Value;
                width += icon.Width * scale;
            }

            string eventName = GetEventName(eventId);
            Vector2 textSize = FontAssets.MouseText.Value.MeasureString(eventName);
            width += textSize.X * scale * 0.8f + 10f * scale;

            return width;
        }

        private static void DrawSeparator(SpriteBatch spriteBatch, Vector2 position)
        {
            string separator = ":";
            Vector2 textSize = FontAssets.MouseText.Value.MeasureString(separator);
            Vector2 textPos = position + new Vector2(0f, textSize.Y * 0.5f);
            Utils.DrawBorderString(spriteBatch, separator, textPos + new Vector2(1f, 1f), Color.Black * 0.6f, 1.0f, 0.5f, 0.5f);
            Utils.DrawBorderString(spriteBatch, separator, textPos, Color.White, 1.0f, 0.5f, 0.5f);
        }

        public override void NetSend(BinaryWriter writer)
        {
            writer.Write(AuroraType);
        }

        public override void NetReceive(BinaryReader reader)
        {
            AuroraType = reader.ReadByte();
        }
    }
}